﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using System.IO;
using System.Xml.Serialization;
using System.Xml;

namespace NexTrackDesktop.Media
{
    /// <summary>
    /// This class holds all song library related info and events.
    /// </summary>
    public class Library : NexTrackDesktop.Media.ILibrary
    {
        private List<Song> _library;
        private HashSet<Uri> _fileUris; // file locations

        public virtual ITagger Tagger { get; set; }
        public virtual ILibrarySource LibrarySource { get; private set; }

        public virtual ReadOnlyCollection<Song> Items
        {
            get
            {
                return new ReadOnlyCollection<Song>(_library);
            }
        }

        /// <summary>
        ///     Creates an empty library with the given file tagger to be used for tagging songs.
        /// </summary>
        /// <param name="tagger">
        ///     Optional. The tagger to use for collecting song tags from input files.
        ///     If tagger is null or missing, some sort of default tagger will be used.
        /// </param>
        /// <param name="librarySource">
        ///     Optional. An ILibraryLoader to load the library from.
        /// </param>
        public Library(ITagger tagger = null, ILibrarySource librarySource = null)
        {
            _fileUris = new HashSet<Uri>();
            _library = new List<Song>();

            if (librarySource != null)
            {
                LibrarySource = librarySource;

                //after loading the library, we take the file URIs and test them.
                _library = new List<Song>(librarySource.Load());
                _fileUris = new HashSet<Uri>(_library.Select(s => s.Location));
            }

            if (tagger == null)
                tagger = new DiscogsArtTagger(new TagLibTagger());

            this.Tagger = tagger;
        }

        /// <summary>
        ///     Adds a song to the library from the given location uri. The song will be tagged
        ///     using this instance's Tagger
        /// </summary>
        /// <param name="uri"></param>
        public virtual void AddFile(Uri uri)
        {
            if (_fileUris.Contains(uri))
                return;

            Song newSong = new Song() { Location = uri };
            Tagger.tag(newSong);

            _fileUris.Add(uri);
            _library.Add(newSong);

            if (SongAdded != null)
                SongAdded(newSong);
        }


        public static string[] searchExtensions = { ".mp3", ".mp4", ".m4a", ".ogg", ".flac", ".cda", ".wma" };
        /// <summary>
        /// This method is called recursivly to add folders within folders to library
        /// </summary>
        /// <param name="path">Path to folder</param>
        public virtual void addFolderRecursive(string path)
        {
            foreach (var file in Directory.GetFiles(path))
            {
                if (searchExtensions.Contains(Path.GetExtension(file)))
                    AddFile(new Uri(file));
            }

            foreach (var folder in Directory.GetDirectories(path))
            {
                addFolderRecursive(folder);
            }
        }

        /// <summary>
        /// Uses the ILibrarySource object to save the library somewhere.
        /// </summary>
        /// <param name="libraryDestination"></param>
        public virtual void SaveLibrary()
        {
            LibrarySource.Save(Items);
        }

        // Events triggered when the songs in the library are modified.        
        public event Action<Song> SongAdded;
        public event Action<Song> SongChanged; //TODO
        public event Action<Song> SongRemoved; //TODO
    }

    public interface ILibrarySource
    {
        IEnumerable<Song> Load();
        void Save(IEnumerable<Song> songs);
    }

    public class LibraryXMLFileSource : ILibrarySource
    {
        public string FileSource { get; private set; }

        public LibraryXMLFileSource(string fileName)
        {
            FileSource = fileName;
            string dir = Path.GetDirectoryName(fileName);

            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            if (!File.Exists(fileName))
                Save(new List<Song>());

        }

        /// <summary>
        /// Loads the song metadata from the source file location.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<Song> Load()
        {
            XmlSerializer xs = new XmlSerializer(typeof(Song[]));

            using (XmlReader xr = XmlReader.Create(FileSource))
            {
                return (IEnumerable<Song>)xs.Deserialize(xr);
            }
        }

        /// <summary>
        /// Saves the song metadata to the source file location.
        /// </summary>
        /// <param name="songs"></param>
        public void Save(IEnumerable<Song> songs)
        {
            XmlSerializer xs = new XmlSerializer(typeof(Song[]));
            XmlWriter xw = XmlWriter.Create(FileSource,
                new XmlWriterSettings() { Indent = true });

            xs.Serialize(xw, songs.ToArray());
            xw.Close();
        }
    }
}